package socket

import (
	"net"
	"time"
)

//socket客户端
type Client struct {
	Conn              *Conn                   //继承连接对象
	onConnect         ClientConnectHandleFunc //连接成功回调
	onMessage         MessageHandleFunc       //消息接收处理回调
	onClose           DefHandleFunc           //关闭连接时的回调
	onTimeout         DefHandleFunc           //超时
	onError           ErrorHandleFunc         //发生错误时的回调、
	headData          []byte                  //请求头
	heartbeat         []byte                  //心跳数据
	heartbeatWatiTime int64                   //超过该时间无数据发送心跳（单位：秒）
	waitCloseTime     int64                   //关闭时间
}

//创建一个客户端
//输入：data=初次连接输入的参数
func NewClient(data []byte) *Client {
	obj := new(Client)
	obj.headData = data
	return obj
}

//发送数据
func (this *Client) Write(bt []byte) (int, error) {
	return this.Conn.Write(bt)
}

//关闭连接
func (this *Client) Close() error {
	return this.Conn.Close()
}

//设置心跳数据
//bt=心跳数据，wait=超过该时间就发送一次心跳
func (this *Client) SetHeartbeat(bt []byte, wait int64, closeTime int64) {
	this.heartbeat = bt
	this.heartbeatWatiTime = wait
	this.waitCloseTime = closeTime
}

//注册回调函数，当发起连接成功时触发
func (this *Client) OnConnect(call ClientConnectHandleFunc) {
	this.onConnect = call
}

//注册回调函数，当接收到消息时触发
func (this *Client) OnMessage(call MessageHandleFunc) {
	this.onMessage = call
}

//当超时时
func (this *Client) OnTimeout(call DefHandleFunc) {
	this.onTimeout = call
}

//注册回调函数，当关闭连接时触发
func (this *Client) OnClose(call DefHandleFunc) {
	this.onClose = call
}

//注册回调函数，当发生错误时触发
func (this *Client) OnError(call ErrorHandleFunc) {
	this.onError = call
}

//发起连接
//输入：addr=发起请求的地址
func (this *Client) Connect(addr string) error {
	c, err := net.Dial("tcp", addr)
	if err != nil { //连接失败
		return err
	}
	this.Conn = newConn(c)
	heartbeatContinue := true
	defer func() {
		ifcall(this.onClose != nil, func() {
			this.onClose(this.Conn)
		})
		heartbeatContinue = false
		this.Conn.Close()
	}()

	try(func() {
		//心跳处理
		go func() {
			if this.heartbeatWatiTime > 0 {
				for {
					if !heartbeatContinue {
						return
					}
					time.Sleep(time.Second * time.Duration(this.heartbeatWatiTime))
					now := time.Now().Unix()
					diffTime := now - this.Conn.LastAccessTime.Unix()
					if diffTime > int64(this.waitCloseTime) { //超过3倍心跳时间则断开连接
						ifcall(this.onTimeout != nil, func() {
							this.onTimeout(this.Conn)
						})
						this.Conn.Close()
					} else if diffTime > int64(this.heartbeatWatiTime) { //超过心跳检查时间，则发送心跳信息
						//print("发送心跳\n")
						this.Write(this.heartbeat) //发送心跳
					}
				}
			}
		}()
		this.Write(this.headData) //发送连接数据
		responseHeadRequest, err := this.Conn.Read()
		if err != nil {
			ifcall(this.onError != nil, func() {
				this.onError(this.Conn, err)
			})
			return
		}
		try(func() {
			this.onConnect(this.Conn, responseHeadRequest)
		}, func(e Error) {
			ifcall(this.onError != nil, func() {
				this.onError(this.Conn, e)
			})
		})
		for {
			readData, err := this.Conn.Read()
			if err != nil {
				break
			}
			//循环体内保证不抛出异常
			go try(func() { //协程执行每个请求
				this.Conn.Lock()                      //加锁保证执行顺序
				defer this.Conn.Unlock()              //
				this.Conn.LastAccessTime = time.Now() //更新链接时间
				//如果是心跳数据
				if len(readData) == len(this.heartbeat) && string(readData) == string(this.heartbeat) {
					//print("响应心跳\n") //忽略

				} else { //否则是普通数据
					ifcall(this.onMessage != nil, func() {
						this.onMessage(this.Conn, readData)
					})
				}
			}, func(e Error) {
				ifcall(this.onError != nil, func() {
					this.onError(this.Conn, e)
				})
			})
		}
	}, func(err Error) {
		ifcall(this.onError != nil, func() {
			this.onError(this.Conn, err)
		})
	})
	return nil
}
